package org.ecpkn.PAGridSim.core.gridsimEntity.policy.paPolicy;

import java.util.Calendar;

import org.ecpkn.PAGridSim.core.configuration.Configuration;
import org.ecpkn.PAGridSim.core.gridsimEntity.paMachine.PAMachine;
import org.ecpkn.PAGridSim.core.gridsimEntity.paMachine.PAMachineList;
import org.ecpkn.PAGridSim.core.gridsimEntity.paResourceCharacteristics.PAResourceCharacteristics;
import org.ecpkn.PAGridSim.core.gridsimEntity.policy.BasicPolicy;
import org.ecpkn.PAGridSim.core.offlineOptimization.OptimizedResultEntry;

import eduni.simjava.Sim_event;

/**
 * This class extend BasicPolicy class with some methods added in order to get
 * the power aware capability
 */

public abstract class PAPolicy extends BasicPolicy {

	/**
	 * Allocates a new PAPolicy object
	 * 
	 * @param resourceName
	 *            GridResource name that will contain this allocation policy
	 * @param policyName
	 *            this object entity name
	 * @throws Exception
	 *             This happens when one of the following scenarios occur:
	 *             creating this entity before initializing GridSim package;
	 *             this entity name is null or empty;this entity has zero number
	 *             of PEs (Processing Elements). No PEs mean the Gridlets can't
	 *             be processed. A GridResource must contain one or more
	 *             Machines. A Machine must contain one or more PEs.
	 * @see gridsim.GridSim#init(int, Calendar, boolean, String[], String[],
	 *      String)
	 * @pre resourceName != null;entityName != null
	 */
	public PAPolicy(String resourceName, String policyName) throws Exception {
		super(resourceName, policyName);
	}

	/**
	 * get the power aware resource characteristics of this policy
	 * 
	 * @return PAResourceCharacteristics
	 */
	public PAResourceCharacteristics getPAResouce() {
		return (PAResourceCharacteristics) this.resource_;
	}

	/**
	 * get the machine in the machinelist of the resource.
	 * 
	 * @param index
	 *            the number of the machine in the list
	 * @return a machine
	 */
	public PAMachine getPAMachineByOrder(int index) {
		return getPAResouce().getPAMachineList().get(index);
	}

	/**
	 * get the machineList composed of all awake machines. With some power aware
	 * policy, some machines can be shut down
	 * 
	 * @return PAMachineList
	 */
	public PAMachineList getAwakePAMachineList() {
		return getPAResouce().getAwakePAMachineList();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gridsim.AllocPolicy#processOtherEvent(eduni.simjava.Sim_event)
	 */
	@Override
	public void processOtherEvent(Sim_event ev) {
		// add a change node frequency event
		if (ev.get_tag() == Configuration.Const.CHANGE_NODE_FREQUENCY) {
			// change UF
			try {
				adaptResourceToOptimizedResult(ev);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * adapt the resource according to optimized result
	 * 
	 * @param event
	 * @throws Exception
	 */
	private void adaptResourceToOptimizedResult(Sim_event ev) throws Exception {

		OptimizedResultEntry data = (OptimizedResultEntry) ev.get_data();
		int nodesNumber = data.getOptimizedNodesNumber_();
		double frequencyOptimized = data.getOptimizedFrequency_();
		// Attention:when fO is too high,(int) cast may lost information
		int f = (int) frequencyOptimized;
		if (nodesNumber > this.resource_.getMachineList().size()) {
			throw new Exception(
					"PAPolicy.adaptResourceToOptimizedResult:the optimized node number is greater than the total node number.Please add total number or decrease the workload");
		}
		double timePoint = data.getTimePoint();
		for (int j = 0; j < this.resource_.getMachineList().size(); j++) {
			PAMachine m = ((PAMachine) this.resource_.getMachineList().get(j));
			m.reset();
			if (j < nodesNumber) {
				// Lin:in my paper,what I means of frequency is in reality
				// instruction per second.
				m.turnOn();
				try {
					m.setFrequency(f / PAMachine.Const.IPC, gridletInExecList_);
					// The partition approach is not work-conserving, some
					// processors may be left idle even if there are unfinished
					// jobs(they can't be assigned to other processors)
					m.setTasksInMachine(data.getModelMachineList().get(j)
							.getTasksInMachine());
					m.updateMachineUtilization(timePoint);
					m.record(timePoint);
				} catch (Exception e) {
					e.printStackTrace();
				}
				// just keep task in Execlist in the machine, autorize the other
				// task to change machine in the future
			} else {
				m.turnOff();
				m.setFrequency(0.0, gridletInExecList_);
				m.record(timePoint);
			}
		}
		getPAResouce().updateAwakePAMachineList();
	}

}
